

#ifndef HANDLES_H
#define	HANDLES_H



#include <boost/scoped_array.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/assert.hpp>

using boost::scoped_array;

#include <vector>

#include "Debugging.h"

#include "Handle.h"
using namespace std;



template<typename _Handle, typename _Data, unsigned int _HandlesTableSize>
class Handles
{
public:
    Handles():
    magicNumbers((std::size_t) _HandlesTableSize,0)
    {
        vData.reset(new _Data[_HandlesTableSize]);
        freeSlots.reserve(_HandlesTableSize);
        freeAll();
    }

    _Handle acquire()
    {
        _Handle newHandle;

        if(!freeSlots.empty())
        {
            unsigned int newIndex=freeSlots.back();
            freeSlots.pop_back();

            newHandle.init(newIndex);

            magicNumbers[newHandle.getIndex()]=newHandle.getMagic();
        }

        BOOST_ASSERT_MSG(!newHandle.isNull(),"cant acquire new handle");

        return newHandle;
    }

    void release(_Handle handleToRelease)
    {
        BOOST_ASSERT_MSG(!handleToRelease.isNull(),"invalid handle released (null handle)");
        BOOST_ASSERT_MSG(handleToRelease.getIndex()<_HandlesTableSize,"invalid handle released (invalid index)");
        BOOST_ASSERT_MSG(handleToRelease.getMagic()==magicNumbers[handleToRelease.getIndex()],"invalid handle released (invalid magic)");

        magicNumbers[handleToRelease.getIndex()]=0;
        freeSlots.push_back(handleToRelease.getIndex());
    }

    _Data* dereference(_Handle handle)
    {
         BOOST_ASSERT_MSG(!handle.isNull(),"invalid handle dereferenced (null handle)");
         BOOST_ASSERT_MSG(handle.getIndex()<_HandlesTableSize,"invalid handle dereferenced (invalid index)");
         BOOST_ASSERT_MSG(handle.getMagic()==magicNumbers[handle.getIndex()],"invalid handle dereferenced (invalid magic)");

         return &vData[handle.getIndex()];
    }

private:
    Handles(Handles&){};
    Handles& operator=(Handles&){};

    scoped_array<_Data> vData;
    vector<unsigned int> magicNumbers;
    vector<unsigned int> freeSlots;


    void freeAll()
    {
        freeSlots.clear();
        for(int i=0;i<_HandlesTableSize;i++)
        {
            freeSlots.push_back(i);
        }
    }

};
#endif	/* HANDLES_H */

